<?php

namespace App\Http\Validate\OpenShop\InvestmentPromotion;

use App\Enums\OpenShop\InvestmentPromotion\UserIdentityCardEnum;
use App\Http\Validate\BaseValidate;
use App\Models\OpenShop\InvestmentPromotion\OpenShopMainProcessModel;
use App\Service\OpenShop\Feishu\ConfigService;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;


/**
 * [ open_shop_user_identity_card 招商加盟 - 用户身份证信息 ] - 校验规则
 *
 * @UserIdentityCardValidate
 * @package App\Http\Controllers
 * @date 2022/02/28 06:49:40
 * @author GreenLeaf <786407418@qq.com>
 */
class UserIdentityCardValidate extends BaseValidate
{

    public static function registerValidateRules(): void
    {
        // TODO: Implement registerValidateRules() method.
        Validator::extend('identityCardNumber',
            function ($attribute, $value, $parameters, \Illuminate\Validation\Validator $validator) {
                return self::isValid($value);
            }, '身份证号码不合法');

        Validator::extend('compareDateTime',
            function ($attribute, $value, $parameters, \Illuminate\Validation\Validator $validator) {
                $validator->requireParameterCount(1, $parameters, 'compareDateTime');
                $comparedToValue = $validator->getData()[$parameters[0]];
                return strtotime($value) >= strtotime($comparedToValue);
            }, '有效期配置非法');
    }

    /**
     * 获取规则信息
     *
     * @param array $fields
     * @return string[]
     */
    public static function getRules(array $fields = []): array
    {
        return self::getActuallyRules([
            'file'               => 'bail|required',
            //身份证正面图
            'type'               => 'bail|required|in:face,back',
            'orderNo'            => 'bail|required|exists:' . OpenShopMainProcessModel::class . ',OrderNo',
            //
            'frontSideImg'       => 'bail|required|string|max:255',
            //身份证正面图
            'backSideImg'        => 'bail|required|string|max:255',
            //身份证背面图
            'name'               => 'bail|required|string|max:12',
            //身份证姓名
            'identityCardNumber' => 'bail|required|string|identityCardNumber|max:19',
            //身份证号码
            'gender'             => 'bail|required|in:' . enumTransToRules(collect(ConfigService::config("openShop")["gender"])
                    ->pluck("name", "id")->toArray()),
            //性别 0:女性 1:男性
            'effectiveStartTime' => 'bail|required|string|date_format:Ymd',
            //身份证有效期 - 开始时间
            'effectiveEndTime'   => 'bail|required|string|date_format:Ymd|compareDateTime:effectiveStartTime',
            //身份证有效期 - 截止时间
            'nativePlace'        => 'bail|required|string|max:18',
            //籍贯
            'nation'             => 'bail|required|string|max:18',
            //民族
            'birthday'           => 'bail|required|string|date_format:Ymd',
            //生日
            'animalSign'         => 'sometimes|required|string|max:2',
            //生肖
            'constellation'      => 'sometimes|required|string|max:4',
            //星座
        ],
            $fields);
    }

    public static array $attributes = [
        'file'               => '身份证正面图',
        'type'               => '类型',
        'orderNo'            => '订单号',
        'frontSideImg'       => '身份证正面图',
        'backSideImg'        => '身份证背面图',
        'name'               => '身份证姓名',
        'identityCardNumber' => '身份证号码',
        'gender'             => '性别',
        'effectiveStartTime' => '身份证有效期 - 开始时间',
        'effectiveEndTime'   => '身份证有效期 - 截止时间',
        'nativePlace'        => '籍贯',
        'nation'             => '民族',
        'birthday'           => '生日',
        'animalSign'         => '生肖',
        'constellation'      => '星座',
    ];

    /**
     * 创建校验
     *
     * @param array $requestParams
     * @return array
     * @throws ValidationException
     */
    public static function insertValidate(array $requestParams): array
    {
        return self::baseValidate(
            $requestParams,
            collect(self::getRules())->except(['orderNo', 'file', 'type'])->keys()->toArray(),
            function ($params) {
                $params["frontSideImg"]       = getPrivateFileKey($params['frontSideImg']);
                $params["backSideImg"]        = getPrivateFileKey($params['backSideImg']);
                $params['effectiveStartTime'] = strtotime($params['effectiveStartTime']);
                $params['effectiveEndTime']   = strtotime($params['effectiveEndTime']);
                return $params;
            }
        );
    }

    /**
     * 更新校验
     *
     * @param array $requestParams
     * @return array
     * @throws ValidationException
     */
    public static function updateValidate(array $requestParams): array
    {
        return self::baseValidate(
            $requestParams,
            collect(self::getRules())->except(['file', 'type'])->keys()->toArray(),
            function ($params) {
                $params["frontSideImg"]       = getPrivateFileKey($params['frontSideImg']);
                $params["backSideImg"]        = getPrivateFileKey($params['backSideImg']);
                $params['effectiveStartTime'] = strtotime($params['effectiveStartTime']);
                $params['effectiveEndTime']   = strtotime($params['effectiveEndTime']);
                return $params;
            }
        );
    }

    /**
     * @param array $requestParams
     * @return array
     * @throws ValidationException
     */
    public static function recognizeValidate(array $requestParams): array
    {
        return self::baseValidate(
            $requestParams,
            ['file', 'type']
        );
    }


    /**
     * 校验身份证号是否合法
     * @param string $num 待校验的身份证号
     * @return bool
     */
    public static function isValid(string $num): bool
    {
        //判断身份证是否是 H开头的,如果是就直接返回 true
        if (str_starts_with($num, 'H')) {
            return true;
        }

        //老身份证长度15位，新身份证长度18位
        $length = strlen($num);
        if ($length == 15) { //如果是15位身份证

            //15位身份证没有字母
            if (!is_numeric($num)) {
                return false;
            }
            // 省市县（6位）
            $areaNum = substr($num, 0, 6);
            // 出生年月（6位）
            $dateNum = substr($num, 6, 6);

        } else {
            if ($length == 18) { //如果是18位身份证

                //基本格式校验
                if (!preg_match('/^\d{17}[0-9xX]$/', $num)) {
                    return false;
                }
                // 省市县（6位）
                $areaNum = substr($num, 0, 6);
                // 出生年月日（8位）
                $dateNum = substr($num, 6, 8);

            } else { //假身份证
                return false;
            }
        }

        //验证地区
        if (!self::isAreaCodeValid($areaNum)) {
            return false;
        }

        //验证日期
        if (!self::isDateValid($dateNum)) {
            return false;
        }

        //验证最后一位
        if (!self::isVerifyCodeValid($num)) {
            return false;
        }

        return true;
    }

    /**
     * 省市自治区校验
     * @param string $area 省、直辖市代码
     * @return bool
     */
    public static function isAreaCodeValid(string $area): bool
    {
        $provinceCode = substr($area, 0, 2);

        // 根据GB/T2260—999，省市代码11到65
        if (11 <= $provinceCode && $provinceCode <= 65) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 验证出生日期合法性
     * @param string $date 日期
     * @return bool
     */
    public static function isDateValid(string $date): bool
    {
        if (strlen($date) == 6) { //15位身份证号没有年份，这里拼上年份
            $date = '19' . $date;
        }
        $year  = intval(substr($date, 0, 4));
        $month = intval(substr($date, 4, 2));
        $day   = intval(substr($date, 6, 2));

        //日期基本格式校验
        if (!checkdate($month, $day, $year)) {
            return false;
        }

        //日期格式正确，但是逻辑存在问题(如:年份大于当前年)
        $currYear = date('Y');
        if ($year > $currYear) {
            return false;
        }
        return true;
    }

    /**
     * 验证18位身份证最后一位
     * @param string $num 待校验的身份证号
     * @return bool
     */
    public static function isVerifyCodeValid(string $num): bool
    {
        if (strlen($num) == 18) {
            $factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
            $tokens = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];

            $checkSum = 0;
            for ($i = 0; $i < 17; $i++) {
                $checkSum += intval($num[$i]) * $factor[$i];
            }

            $mod   = $checkSum % 11;
            $token = $tokens[$mod];

            $lastChar = strtoupper($num[17]);

            if ($lastChar != $token) {
                return false;
            }
        }
        return true;
    }

}
